//
//  FileCache.swift
//  ZLKit_Swift
//
//  Created by itzhaolei on 2022/1/18.
//

import Alamofire

// API调用时用到
public typealias NetworkService = Network.Service
// 配置网络请求时用到
public typealias NetworkEnvironment = Network.Environment
// 监听网络环境变化时用到
public typealias NetworkStatus = Alamofire.NetworkReachabilityManager.NetworkReachabilityStatus
public let NotificationUserInfoNetworkStatusKey = "NotificationUserInfoNetworkStatusKey"

public enum Network { }

// 请求
public extension Network {
    
    enum RequestedState {
        case normal
        case noNetwork
        case timeout
        case serverFailure
    }
    
    class Service: NSObject {
        
        static public let shared: Service = .init()
                
        @discardableResult
        /// 发起请求
        /// - Parameters:
        ///   - request: 请求体
        ///   - completion: 处理下文
        open func request<T>(request: URLRequestConvertible, completion: Params_three_void<RequestedState, T?, Int>) -> DataRequest {
            
            if let validateRequest = try? request.asURLRequest() {
                if Environment.shared.log {
                    print("\(NetworkService.classForCoder()): \(validateRequest.url?.absoluteString ?? "")")
                }
            }
                                
            SessionManager.default.delegate.sessionDidReceiveChallenge = { session,challenge in
                return (URLSession.AuthChallengeDisposition.useCredential,URLCredential(trust:challenge.protectionSpace.serverTrust!))
            }
                        
            return Alamofire.request(request).responseJSON { response in
                switch response.result {
                case .success:
                    if Environment.shared.log {
                        let data = try? JSONSerialization.data(withJSONObject: response.value ?? Dictionary<String, Any>(), options: JSONSerialization.WritingOptions.prettyPrinted)
                        let resultsStr = String.init(data: data ?? .init(), encoding: .utf8)
                        print("\(resultsStr ?? "null")")
                    }
                    completion?(.normal, response.value as? T, response.response?.statusCode ?? 0)
                    break
                case .failure(let error):
                    let message = error as NSError
                    if message.localizedDescription.contains("已断开") || message.localizedDescription.contains("disconnected") {
                        completion?(.noNetwork, response.value as? T, response.response?.statusCode ?? 0)
                        break;
                    }
                    if message.localizedDescription.contains("超时") || message.localizedDescription.contains("timeout") {
                        completion?(.timeout, response.value as? T, response.response?.statusCode ?? 0)
                        break;
                    }
                    completion?(.serverFailure, response.value as? T, response.response?.statusCode ?? 0)
                    break
                }
            }
        }
                
    }
    
}

// 环境配置
public extension Network {
    
    class Environment: NSObject {
        
        public static let shared: Environment = .init()
        
        /// 所处环境
        open var onEnvironment: Network.EnvironmentType = .auto
        
        /// 域名配置 (网络地址前缀)
        open var domain: String = ""
        
        /// 是否开启日志打印
        open var log: Bool = false
        
        /// 网络状态变化监听者
        private let networkManager = NetworkReachabilityManager()
        
        public override init() {
            super.init()
            networkManager!.listener = { status in
                postNotification(.deviceNetworkChanged, userInfo: [NotificationUserInfoNetworkStatusKey:status])
            }
            networkManager!.startListening()
        }
        
        /// 注册域名
        /// - Parameters:
        ///   - api_debug: API前缀
        ///   - api_release: API前缀
        ///   - on: 当前要运行的API环境
        ///   - log: 是否开启日志打印
        static public func registerDomain(debug: String, release: String, on: EnvironmentType = .auto, log: Bool = false) {
            Environment.shared.log = log
            switch on {
                case .auto:
                    #if DEBUG
                    Environment.shared.onEnvironment = .debug
                    Environment.shared.domain = debug
                    #else
                    Environment.shared.onEnvironment = .release
                    Environment.shared.domain = release
                    #endif
                    return
                case .debug:
                    Environment.shared.onEnvironment = .debug
                    Environment.shared.domain = debug
                    return
                case .release:
                    Environment.shared.onEnvironment = .release
                    Environment.shared.domain = release
                    return
            }
        }
        
    }
    
    enum EnvironmentType {
        case auto
        case debug
        case release
    }
    
}

// 下载
public extension Network {
    
    class Download: NSObject {
        
        @discardableResult
        public func request<Identifier>(from: URLRequestConvertible, toPath: String? = nil, identifier: Identifier, completion: Params_three_void<RequestedState, Identifier, String?>) -> DownloadRequest {
            var target: DownloadRequest.DownloadFileDestination? = nil
            if let _ = toPath {
                target = { temporaryURL, response in
                    return (destinationURL: .init(fileURLWithPath: toPath ?? ""), options: [.createIntermediateDirectories, .removePreviousFile])
                }
            }else {
                target = DownloadRequest.suggestedDownloadDestination()
            }
            
            SessionManager.default.delegate.sessionDidReceiveChallenge = { session,challenge in
                return (URLSession.AuthChallengeDisposition.useCredential,URLCredential(trust:challenge.protectionSpace.serverTrust!))
            }
            
            return Alamofire.download(from, to: target).response { response in
                if response.error == nil {
                    completion?(.normal, identifier, toPath)
                    return
                }
                let message = response.error?.localizedDescription ?? ""
                if message.contains("已断开") || message.contains("disconnected") {
                    completion?(.noNetwork, identifier, nil)
                    return
                }
                if message.contains("超时") || message.contains("timeout") {
                    completion?(.timeout, identifier, nil)
                    return
                }
                completion?(.serverFailure, identifier, nil)
            }
        }
        
    }
    
}

// 上传
public extension Network {
    
    class Upload: NSObject {
        
        @discardableResult
        public func request<Identifier>(fromPath: String, to: URLRequestConvertible, identifier: Identifier, completion: Params_three_void<RequestedState, Identifier, DefaultDataResponse?>) -> DataRequest {
            
            SessionManager.default.delegate.sessionDidReceiveChallenge = { session,challenge in
                return (URLSession.AuthChallengeDisposition.useCredential,URLCredential(trust:challenge.protectionSpace.serverTrust!))
            }
            
            return Alamofire.upload(.init(fileURLWithPath: fromPath), with: to).response { response in
                if response.error == nil {
                    completion?(.normal, identifier, response)
                    return
                }
                let message = response.error?.localizedDescription ?? ""
                if message.contains("已断开") || message.contains("disconnected") {
                    completion?(.noNetwork, identifier, nil)
                    return
                }
                if message.contains("超时") || message.contains("timeout") {
                    completion?(.timeout, identifier, nil)
                    return
                }
                completion?(.serverFailure, identifier, nil)
            }
        }
        
    }
    
}

public extension URLRequestConvertible {
    
    /// 域名下拼接路径
    /// - Parameter path: 需要拼接的路径
    /// - Returns: 网络访问路径
    func apiDomainAppending(path: String?) -> URL {
        var url = URL(string: Network.Environment.shared.domain) ?? .init(fileURLWithPath: "")
        if let path = path {
            url = url.appendingPathComponent(path)
        }
        return url
    }
    
    /// 全路径下拼接参数（通常用于GET请求）
    /// - Parameter url: 全路径
    /// - Parameter params: 将要拼接在全路径下的参数字符串
    /// - Returns: 携带参数的URL
    func appending(url: URL, params: String) -> URL {
        .init(string: "\(url.absoluteString)\(params)") ?? .init(fileURLWithPath: "")
    }
    
    /// 根据参数生成请求体
    /// - Parameters:
    ///   - url: 请求地址
    ///   - method: 请求方式
    ///   - allHTTPHeaderFields: 请求头
    ///   - params: 请求附带参数
    /// - Returns: 请求体
    func request(url: URL, method: HTTPMethod, allHTTPHeaderFields: [String : String]?, params: [String: Any]?, syncHttpBody: Params_one_return<Data?, [String: String]?> = nil) -> URLRequest {
        let encoding: ParameterEncoding = {
            switch self {
            default:
                return JSONEncoding.default
            }
         }()
        var urlRequest = URLRequest(url: url)
        urlRequest.allHTTPHeaderFields = allHTTPHeaderFields
        urlRequest.httpMethod = method.rawValue
        urlRequest.timeoutInterval = 30
                
        if Network.Environment.shared.log {
            let data = try? JSONSerialization.data(withJSONObject: params ?? Dictionary<String, Any>(), options: JSONSerialization.WritingOptions.prettyPrinted)
            let resultsStr = String.init(data: data ?? .init(), encoding: .utf8)
            print("Params:\(resultsStr ?? "null")")
        }
        
        var request = URLRequest(url: .init(fileURLWithPath: ""))
        
        do {
            request = try encoding.encode(urlRequest, with: params)
        }catch {
            if Network.Environment.shared.log {
                print("\(NetworkService.classForCoder()): Encoding failure .")
            }
            request = .init(url: .init(fileURLWithPath: ""))
        }
        
        if let syncHttpBody = syncHttpBody {
            let newHTTPHeaderFields = syncHttpBody(request.httpBody)
            var allHTTPHeaderFields = allHTTPHeaderFields ?? [:]
            for (key,value) in newHTTPHeaderFields ?? [:] {
                allHTTPHeaderFields[key] = value
            }
            request.allHTTPHeaderFields = allHTTPHeaderFields
        }
        
        if Network.Environment.shared.log {
            let data = try? JSONSerialization.data(withJSONObject: request.allHTTPHeaderFields ?? [:], options: JSONSerialization.WritingOptions.prettyPrinted)
            let resultsStr = String.init(data: data ?? .init(), encoding: .utf8)
            print("HTTPHeader:\(resultsStr ?? "null")")
        }
        
        return request
    }
    
}
